/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package MIPS;

import java.util.concurrent.Semaphore;

/**
 *
 * @author Keiner - jorgue
 */
public class Cache {
    
    private Semaphore sem = new Semaphore(1);
    private volatile Bloque[] cache = new Bloque[4];
    private int[][] memoria;
    private int retraso = 0;
    private int retrasoFallo = 0;
    private int inicioMemoria;

    Cache(int[][] mem, int ret, int iniMem) {
        retrasoFallo = ret;
        memoria = mem;
        inicioMemoria = iniMem;
        for (int i = 0; i < cache.length; i++) {
            cache[i] = new Bloque();
        }
    }

    public int[] deme(int direccion) {
        direccion -= inicioMemoria;
        direccion /= 4;
        int[] tmp = new int[4];
        int numBloque = (int)(direccion/2);
        Bloque actual = cache[numBloque % 4];
        if ((direccion != actual.getDireccion() && direccion != actual.getDireccion()+1) || actual.getDireccion() == -1 ) {
            retraso += retrasoFallo;
            actualizarBloque(direccion, actual);
        }else{
            if(retraso > 0){
                retraso--;
            }
        }       
        if (actual.getDireccion() == direccion) {
            tmp = actual.getPalabra1();
        } else {
            if (actual.getDireccion() + 1 == direccion) {
                tmp = actual.getPalabra2();
            }
        }
        return tmp;
    }

    void guardarDato(int dato, int direccion) {
        direccion -= inicioMemoria;
        int[] tmp = new int[]{dato, 0, 0, 0};
        int numBloque = (int) (direccion / 8);
        Bloque actual = cache[numBloque % 4];
        if (numBloque == (int) (actual.getDireccion() / 8)) {
            if (actual.getDireccion() == direccion) {
                actual.setPalabra1(tmp);
            } else {
                if (actual.getDireccion() + 4 == direccion) {
                    actual.setPalabra2(tmp);
                }
            }
        }
        System.arraycopy(tmp, 0, memoria[numBloque], 0, tmp.length);
    }

    void actualizarBloque(int direccion, Bloque actual) {      
        actual.setDireccion(direccion);
        actual.setPalabra1(memoria[direccion]);
        actual.setPalabra2(memoria[direccion + 1]);
    }

    void cambioCiclo() {
        retraso--;
    }

    public int getRetraso() {
        return retraso;
    }

    public void setRetraso(int retraso) {
        this.retraso = retraso;
    }

    public Bloque[] getCache() {
        return cache;
    }

    public void setCache(Bloque[] cache) {
        this.cache = cache;
    }

    public Semaphore getSem() {
        return sem;
    }

    public void setSem(Semaphore sem) {
        this.sem = sem;
    }

    private class Bloque {

        private volatile int direccion;
        private volatile int[][] palabras = new int[2][4];

        public Bloque() {
            direccion = -1;
        }

        public int getDireccion() {
            return direccion;
        }

        public void setDireccion(int direccion) {
            this.direccion = direccion;
        }

        public int[] getPalabra1() {
            return palabras[0];
        }

        public void setPalabra1(int[] palabra) {
            System.arraycopy(palabra, 0, this.palabras[0], 0, palabra.length);
        }

        public int[] getPalabra2() {
            return palabras[1];
        }

        public void setPalabra2(int[] palabra) {
            System.arraycopy(palabra, 0, this.palabras[1], 0, palabra.length);
        }
    }
}
